/*
 * Copyright (c) Huawei Device Co., Ltd. 2024. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "access_rule_driver.h"
#include "response_refresh_tag_do.h"
#include "common_utils.h"

namespace OHOS {
namespace SE {
using namespace std;
std::shared_ptr<ResponseRefreshTagDo> g_reFreshTagDo;
constexpr int VALUE_OF_HEX80 = 0x80;
constexpr int VALUE_OF_HEX100 = 0x0100;
constexpr int VALUE_OF_HEX10000 = 0x010000;
constexpr int VALUE_OF_HEX1000000 = 0x01000000;
constexpr int BYTE_MASK = 0xFF;
constexpr int BYTE_BIT_NUM = 8;
constexpr int TWO_BYTES_NUM = 16;
static constexpr int MIN_PAD_LENGTH = 2;

/*
 * Software that is part of the Secure Element access API, it obtains access
 * rules from the Secure Element and applies those rules to restrict device
 * application access to the various Secure Element applications.
 */
AccessRuleDriver::AccessRuleDriver(sptr<OMAPI::ISecureElementChannel> channelImpl)
{
    channelImpl_ = channelImpl;
}

AccessRuleDriver::~AccessRuleDriver()
{
}

bool AccessRuleDriver::GetApduResponseSW(const std::vector<uint8_t> &response)
{
    uint16_t len = response.size();
    InfoLog("response size = %{public}d.", len);
    uint8_t mask = 0xFF;
    if (len < APDU_SW_BYTES) {
        ErrorLog("response is invalid.");
        return false;
    }
    if ((response[len - APDU_SW_BYTES] & mask) == 0x90 && (response[len - 1] & mask) == 0x0) {
        return true;
    }
    return false;
}

int AccessRuleDriver::ReadRefreshTag(std::vector<uint8_t> &retRefreshTag)
{
    std::vector<uint8_t> refreshTagCmd = {0x80, 0xCA, 0xDF, 0x20, 0x0};
    std::vector<uint8_t> response;
    int ret = SendApdu(refreshTagCmd, response);
    if (ret != BER_RET_SUCCESS) {
        return ret;
    }
    std::vector<uint8_t> data(response.begin(), response.end() - APDU_SW_BYTES);
    ResponseRefreshTagDo refreshTagDo = ResponseRefreshTagDo(data);
    uint32_t tmpTag = 0;
    if (!refreshTagDo.GetDoTag(tmpTag) || tmpTag != RESPONSE_REFRESH_TAG_DO) {
        ErrorLog("failed to get refreshTagDo header.");
        return BER_RET_HEADER_ERROR;
    }
    if (!refreshTagDo.GetDoPayload(retRefreshTag)) {
        ErrorLog("failed to get refreshTagDo payload.");
        return BER_RET_PAYLOAD_ERROR;
    }
    return BER_RET_SUCCESS;
}

int AccessRuleDriver::ReadAllAccessRules(std::vector<uint8_t> &accessRules)
{
    std::vector<uint8_t> getAllCmd = {0x80, 0xCA, 0xFF, 0x40, 0};
    std::vector<uint8_t> response;
    int ret = SendApdu(getAllCmd, response);
    if (ret != 0) {
        return ret;
    }
    // std::vector<uint8_t> data(response.begin(), response.end() - 2);
    if (!GetApduResponseSW(response)) {
        return -1;
    }
    accessRules.assign(response.begin(), response.end() - APDU_SW_BYTES);
    return 0;
}

void AccessRuleDriver::SaveRefreshTag(const std::vector<uint8_t> &refreshTag)
{
    saveRefreshTag_.assign(refreshTag.begin(), refreshTag.end());
}

std::vector<uint8_t> AccessRuleDriver::GetRefreshTag()
{
    return saveRefreshTag_;
}

void AccessRuleDriver::UpdateChannel(sptr<OMAPI::ISecureElementChannel> channelImpl)
{
    channelImpl_ = channelImpl;
}

int AccessRuleDriver::SendApdu(std::vector<uint8_t> cmd, std::vector<uint8_t> &responseVec)
{
    std::string cmdStr = CommonUtils::BytesArrayToHexString(&cmd[0], cmd.size());
    DebugLog("cmdStr = %{public}s.", cmdStr.c_str());
    std::string response;
    ErrorCode ret = channelImpl_->Transmit(cmdStr, response);
    CommonUtils::HexStringToBytesArray(response, responseVec);
    return ret;
}

int AccessRuleDriver::PaddingBytes(std::vector<uint8_t> data, uint32_t totalLength, std::vector<uint8_t> retVec)
{
    if (data.size() == totalLength) {
        return 0;
    }
    if (data.size() > totalLength) {
        return -1;
    }
    uint32_t padLenth = totalLength - data.size();
    if (padLenth < MIN_PAD_LENGTH) {
        return -1;
    }
    // This case is invalid, (drop 0x81)007F00.../(drop 0x83)008180...
    // split 2 TLV, first 000100.., second 007D00
    uint32_t dataLen = padLenth - 2;
    if ((dataLen == VALUE_OF_HEX80) || (dataLen == VALUE_OF_HEX100) ||
        (dataLen == VALUE_OF_HEX10000)) {
        std::vector<uint8_t> tmpData = {0x00, 0x01, 0x00};
        retVec.insert(retVec.end(), tmpData.begin(), tmpData.end());
        if (retVec.size() > totalLength) {
            return -1;
        }
        padLenth = totalLength - retVec.size();
    }
    uint32_t valLen = 0;
    std::vector<uint32_t> padData(padLenth, 0);
    if (padLenth - 2 < VALUE_OF_HEX80) { // 2 means 1byte tag and 1byte len
        // 0 <= TLV value length <= 0x7F
        valLen = padLenth - 2; // 2 means 1byte tag and 1byte len
        padData[1] = valLen & BYTE_MASK;
    } else if (padLenth - 3 < VALUE_OF_HEX100) { // 3 = 1 byteTAG + 2 byteLen(0x81xx)
        // 0x80 <= TLV value length <= 0xFF
        valLen = padLenth - 3; // 3 = 1 byteTAG + 2 byteLen(0x81xx)
        padData[1] = 0x81;
        padData[2] = valLen & BYTE_MASK; // 2 save Actual length
    } else if (padLenth - 4 < VALUE_OF_HEX10000) { // 4 = 1 byteTAG + 3 byteLen(0x82xxyy)
        // 0x0100 <= TLV value length <= 0xFFFF
        valLen = padLenth - 4; // 4 = 1 byteTAG + 3 byteLen(0x82xxyy)
        padData[1] = 0x82;
        padData[2] = (valLen >> BYTE_BIT_NUM) & BYTE_MASK; // 2 save high-order byte
        padData[3] = valLen & BYTE_MASK; // 3 save low-order byte
    } else if (padLenth - 5 < VALUE_OF_HEX1000000) { // 5 = 1 byteTAG + 4 byteLen(0x83xxyyzz)
        // 0x010000 <= TLV value length <= 0xFFFFFF
        valLen = padLenth - 5; // 5 = 1 byteTAG + 4 byteLen(0x83xxyyzz)
        padData[1] = 0x83;
        padData[2] = (valLen >> TWO_BYTES_NUM) & BYTE_MASK; // 2 save most significant byte
        padData[3] = (valLen >> BYTE_BIT_NUM) & BYTE_MASK; // 3 save intermediate byte
        padData[4] = valLen & BYTE_MASK; // 4 save least significant byte
    } else {
        return -1;
    }
    retVec.insert(retVec.end(), padData.begin(), padData.end());
    return 0;
}

}  // namespace SE
}  // namespace OHOS
